package com.lry;

import com.lry.task.Task;
import com.lry.task.TaskStrategy;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;

@Slf4j
public class Node {
    private String name;//节点名字
    private Node parent;//父亲节点
    private Map<String,Node> nodeMap;//nodeName,Node
    private List<Node> nodes;//all nodes

    private List<Node> children;//孩子节点nodes
    private List<Node> others;//生成最小生成树后排除在生成树之外的节点
    private SegmentLock<String> segmentLock;

    public Node(String name){
        this.nodeMap = new ConcurrentHashMap<>();
        this.name = name;//name不能重复
        this.nodes = new ArrayList<>();
        this.children = new ArrayList<>();
        this.others = new ArrayList<>();
        this.segmentLock = new SegmentLock<>();
    }

    public void registerNodes(List<Node> inNodes,Node exceptNode){
        for (Node node : inNodes) {
            if(null==node||node==exceptNode){
                continue;
            }
            nodes.add(node);
            nodeMap.put(node.getName(),node);
        }
    }

    public void registerNodes(Node... inNodes){
        for (Node node : inNodes) {
            if(null==node){
                continue;
            }
            nodes.add(node);
            nodeMap.put(node.getName(),node);
        }
    }


    //根节点苏醒，开始flooding构造最小生成树
    public void wakeup(){
        log.info("{}节点苏醒，flooding构造最小生成树....",name);
        this.parent = this;

        for (Node node : nodes) {
            CompletableFuture.runAsync(()->{
                log.info("{}节点发送m消息给{}",this.name,node.getName());
                node.receiveMsg(new Msg(this.name,MsgEnums.M.getMsg()));
            });
        }

        while(Start.successNum.intValue()<nodes.size()){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public void dfsWakeup(){
        log.info("{}节点苏醒，dfs构造最小生成树....",name);
        this.parent = this;
        Node node = nodes.remove(0);
        if(null==node){
            return;
        }
        log.info("{}节点发送m消息给{}",this.name,node.getName());
        node.dfsReceiveMsg(new Msg(this.name,MsgEnums.M.getMsg()));
    }


    public void dfsReceiveMsg(Msg msg){
        log.info("{},receiveMsg,msg={}",this.name,msg);
        Node fromNode = nodeMap.get(msg.getNodeName());
        if(null==fromNode){
            log.error("{}，receiveMsg#fromNode is null,msg={}",this.name,msg);
            return;
        }
        String message = msg.getMsg();

        if(MsgEnums.M.getMsg().equals(message)) {
            if(null==this.parent){
                log.info("{}节点设置parent={}",this.name,fromNode.getName());
                //set up parent
                this.parent = fromNode;
                this.nodes.remove(fromNode);

                //优化处：广播给所有邻居，告诉他我有父亲了，你不要再给我发m消息了
                for (Node node : this.nodes) {
                    CompletableFuture.runAsync(()->{
                        node.dfsReceiveMsg(new Msg(this.name,MsgEnums.HAS_PARENT.getMsg()));
                    });
                }
                //如果邻居为空，则send parent回溯
                if(this.nodes.isEmpty()){
                    fromNode.dfsReceiveMsg(new Msg(this.name,MsgEnums.PARENT.getMsg()));
                }else{
                    //选第一个邻居发送m
                    Node node = this.nodes.remove(0);
                    node.dfsReceiveMsg(new Msg(this.name,MsgEnums.M.getMsg()));
                }
            }else{
                log.info("{}节点发送reject消息给节点{}",this.name,fromNode.getName());
                //send reject msg to fromNode
                fromNode.dfsReceiveMsg(new Msg(this.name,MsgEnums.REJECT.getMsg()));
            }
        }else if(MsgEnums.PARENT.getMsg().equals(message)) {
            this.children.add(fromNode);
            if(this.nodes.isEmpty()){
                if(this.parent==this){
                    return;
                }else{
                    this.parent.dfsReceiveMsg(new Msg(this.name,MsgEnums.PARENT.getMsg()));
                }
            }else{
                //选第一个邻居发送m
                Node node = this.nodes.remove(0);
                node.dfsReceiveMsg(new Msg(this.name,MsgEnums.M.getMsg()));
            }
        }else if(MsgEnums.REJECT.getMsg().equals(message)) {
            if(this.nodes.isEmpty()){
                if(this.parent==this){
                    return;
                }else{
                    this.parent.dfsReceiveMsg(new Msg(this.name,MsgEnums.PARENT.getMsg()));
                }
            }else{
                //选第一个邻居发送m
                Node node = this.nodes.remove(0);
                node.dfsReceiveMsg(new Msg(this.name,MsgEnums.M.getMsg()));
            }
        }else if(MsgEnums.HAS_PARENT.getMsg().equals(message)){
            this.nodes.remove(fromNode);
        }
    }

    public void receiveMsg(Msg msg){
        log.info("{},receiveMsg,msg={}",this.name,msg);
        Node fromNode = nodeMap.get(msg.getNodeName());
        if(null==fromNode){
            log.error("{}，receiveMsg#fromNode is null,msg={}",this.name,msg);
            return;
        }
        String message = msg.getMsg();
        if(MsgEnums.M.getMsg().equals(message)){
            //收到m消息，说明发送者希望我成为他的孩子

            //如果我没有父亲，那么我愿意成为他的孩子
            if(null==this.parent){
                try{
                    segmentLock.lock(this.name);
                    if(null==this.parent){
                        log.info("{}节点设置parent={}",this.name,fromNode.getName());
                        //set up parent
                        this.parent = fromNode;

                        log.info("{}节点发送parent消息给节点{}",this.name,fromNode.getName());
                        //send parent msg to fromNode
                        fromNode.receiveMsg(new Msg(this.name,MsgEnums.PARENT.getMsg()));

                        //send M msg to all nodes except parentNode (fromNode)
                        for (Node node : nodes) {
                            if(node==fromNode){
                                continue;
                            }
                            CompletableFuture.runAsync(()->{
                                log.info("{}节点 发送m消息给{}",this.name,node.getName());
                                node.receiveMsg(new Msg(this.name,MsgEnums.M.getMsg()));
                            });
                        }
                    }
                }finally {
                    segmentLock.unlock(this.name);
                }
            // 已经有父亲了，则拒绝上游节点
            }else{
                log.info("{}节点发送reject消息给节点{}",this.name,fromNode.getName());
                //send reject msg to fromNode
                fromNode.receiveMsg(new Msg(this.name,MsgEnums.REJECT.getMsg()));
            }
        }else if(MsgEnums.PARENT.getMsg().equals(message)){
            log.info("{}节点收到节点{}的parent消息",this.name,fromNode.getName());

            //收到parent，消息，说明发消息的人愿意成为我的孩子，将他加到我的children集合
            children.add(fromNode);

            //如果others.size()+children.size()+1==nodes.size(),算法结束
            if(others.size()+children.size()+1==nodes.size()){
                log.info("{}节点算法结束",this.name);
                Start.successNum.increment();
            }
        }else if(MsgEnums.REJECT.getMsg().equals(message)){
            log.info("{}节点收到节点{}的reject消息",this.name,fromNode.getName());

            //收到reject，消息，说明发消息的人不愿意成为我的孩子，将他加到我的others集合
            others.add(fromNode);

            //如果others.size()+children.size()+1==nodes.size(),算法结束
            if(others.size()+children.size()+1==nodes.size()){
                log.info("{}节点算法结束",this.name);
                Start.successNum.increment();
            }
        }
    }

    public String getName() {
        return name;
    }

    public int getChildrenNum(){
        return children.size();
    }

    @Override
    public String toString() {
        String childStr = "";
        for (Node child : children) {
            childStr = childStr+" "+child.getName();
        }

        return "Node{" +
                "name='" + name + '\'' +
                ", children=" + childStr +
                ", parent=" + parent.getName() +
                '}';
    }


    public List<Integer> submitTask(Task task) {
        TaskStrategy taskStrategy = task.getTaskStrategy();

        List<Integer> list = task.getList();

        int calculateNodes = 1;
        //计算当前节点下总共有多少个’计算节点‘
        for (Node child : children) {
            calculateNodes += child.getChildrenNum();
            calculateNodes += 1;
        }

        Map<Node,List<Integer>> map = new HashMap<>();
        int onceSize = list.size()/calculateNodes;
        map.put(this,list.subList(0,onceSize));
        int curIndex = onceSize;

        int index = 0;
        for (Node child : children) {
            int end = curIndex+(child.getChildrenNum()+1)*onceSize;
            if(index==children.size()-1){
                end = list.size();
            }

            map.put(child,list.subList(curIndex,end));
            curIndex = end;
            index++;
        }

        List<Integer> combineList = new ArrayList<>();
        combineList.addAll(taskStrategy.execute(map.get(this)));


        List<CompletableFuture<List<Integer>>> futures = new ArrayList<>();
        for (Node child : children) {
            CompletableFuture<List<Integer>> future = CompletableFuture.supplyAsync(()->{
                return child.submitTask(new Task(map.get(child),taskStrategy));
            });
            futures.add(future);
        }

        for (CompletableFuture<List<Integer>> future : futures) {
            try {
                combineList.addAll(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        return taskStrategy.execute(combineList);
    }
}
